import { Plugin } from 'rollup'
import { name as pkgName } from '../package.json'
import { parseGlobConfig, resolveGlob, resolveOptions } from './options'
import { GlobConfig, UserOptions } from './types'
import { toArray } from './utils'
export * from './types'

export async function generateGlobImportModule(config: GlobConfig) {
  const globConfig = parseGlobConfig(config, resolveOptions())
  try {
    return (await globConfig?.generateCode()) ?? ''
  } catch (error) {
    console.error(error)
  }
  return ''
}

export default function globImport(options?: UserOptions): Plugin {
  let isWatchMode: boolean
  const resolvedOptions = resolveOptions(options)
  toArray(options?.globConfigs).forEach((glob) => resolveGlob(glob, resolvedOptions))

  const globImporterPrefix = 'GLOB-IMP:'
  const globImporterRe = /^GLOB-IMP:(?<id>[\w=/+]+)\?(?<importer>[\w=/+]+)$/

  const generateGlobImporterId = (id: string, importer: string) => {
    return (
      globImporterPrefix +
      Buffer.from(id).toString('base64') +
      '?' +
      Buffer.from(importer).toString('base64')
    )
  }
  const resolveGlobImporterId = (rawId: string) => {
    const match = rawId.match(globImporterRe)
    if (match) {
      const id = Buffer.from(match.groups!.id, 'base64').toString()
      const importer = Buffer.from(match.groups!.importer, 'base64').toString()
      return { id, importer }
    }
  }

  return {
    name: pkgName,
    options() {
      isWatchMode = this.meta.watchMode
      return null
    },
    configResolved(config: any) {
      isWatchMode = config.command === 'serve'
    },
    async resolveId(source, importer) {
      let id = resolvedOptions.resolveVirtualModuleId(source)
      if (id != null) return id

      const g = resolveGlobImporterId(source)
      if (g) {
        id = g.id
        if (resolveGlob(id, resolvedOptions)) {
          return source
        }
      } else {
        const glob = resolveGlob(source, resolvedOptions, importer)
        if (glob) {
          if (!importer || source === glob.id || !glob.loadByImporter) {
            return glob.id
          }
          return generateGlobImporterId(glob.id, importer)
        }
      }
    },
    async load(id) {
      let code = resolvedOptions.loadVirtualModule(id)
      if (code != null) return code

      let importer: string | undefined
      const g = resolveGlobImporterId(id)
      if (g) {
        id = g.id
        importer = g.importer
      }
      const glob = resolveGlob(id, resolvedOptions)
      if (glob) {
        await glob.start(this)

        if (!isWatchMode) {
          glob.close()
        }
        if (importer) {
          return await glob.loadByImporter?.call(this, importer, glob)
        }
      }
    },
    transform(code, id) {
      const glob = resolveGlob(id, resolvedOptions)
      if (glob) {
        return glob.transform?.call(this, code, id)
      }
    },
  } as Plugin & Record<string, any>
}
